package org.webdb.jdbc;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.webdb.core.Document;
import org.webdb.core.DocumentRecord;
import org.webdb.core.Filter;
import org.webdb.core.Result;
import org.webdb.core.database.Record;
import org.webdb.jdbc.impl.JDBCSelectQuery;

import com.healthmarketscience.sqlbuilder.InsertQuery;

public class JDBCDocument implements Document {

    private JDBCSqlProvider sqlProvider;
    private JDBCTable table;
    private JDBCProvider jdbcProvider;

    public JDBCDocument(JDBCTable table, JDBCSqlProvider sqlProvider,
            JDBCProvider jdbcProvider) {
        this.table = table;
        this.sqlProvider = sqlProvider;
        this.jdbcProvider = jdbcProvider;
    }

    public void close() throws IOException {
        throw new UnsupportedOperationException();
    }

    public Result<Record> getRecords() {
        throw new UnsupportedOperationException();
    }

    public Result<Record> getRecords(Filter filter) {
        throw new UnsupportedOperationException();
    }

    public DocumentRecord save(Record record) {
        if (!table.hasPrimaryKeys()) {
            throw new JDBCException(toString() + " " + table.toString()
                    + " hasn't primary key!");
        }

        if (record.isEmpty())
            throw new IllegalArgumentException("Record hasn't fields!");

        InsertQuery insertQuery = sqlProvider.insertInto(table);
        List<String> userColumns = new LinkedList<String>();
        String userColumn;
        for (Entry<String, Object> entry : record.fieldEntrySet()) {
            insertQuery.addColumn(
                    sqlProvider.column(table, userColumn = entry.getKey()),
                    entry.getValue());
            userColumns.add(userColumn);
        }

        // Check User Defined Primary...
        Map<JDBCColumn, Object> autoPrimaryKeys = null;
        for (JDBCColumn primaryKey : table.getPrimaryKeys()) {
            if (primaryKey.hasAutoStrategy()
                    && !userColumns.contains(primaryKey.getName())) {

                if (autoPrimaryKeys == null)
                    autoPrimaryKeys = new HashMap<JDBCColumn, Object>();

                Object value;
                try {
                    autoPrimaryKeys.put(
                            primaryKey,
                            value = primaryKey.getStrategy().generateValue(
                                    sqlProvider, jdbcProvider));
                } catch (SQLException e) {
                    throw new JDBCException(toString() + " AutoValue "
                            + primaryKey, e);
                }

                insertQuery.addColumn(sqlProvider.getColumn(primaryKey), value);
            }
        }

        JDBCExecutionContext context = jdbcProvider.execute(insertQuery);
        JDBCSelectQuery select = sqlProvider.select(table);
        select.addAllTableColumns(sqlProvider.getTable(table));

        if (autoPrimaryKeys != null) {
            for (Entry<JDBCColumn, Object> entry : autoPrimaryKeys.entrySet()) {
                select.addCondition(sqlProvider.eq(table, entry.getKey(),
                        entry.getValue()));
            }
        } else {
            if (context.hasGeneratedKeys()) {
                int c = 0;
                for (JDBCColumn column : table.getPrimaryKeys()) {
                    select.addCondition(sqlProvider.eq(table, column,
                            context.getGeneratedKey(0, c++)));
                }
            } else {
                for (JDBCColumn column : table.getPrimaryKeys()) {
                    select.addCondition(sqlProvider.eq(table, column,
                            record.getField(column.getName())));
                }
            }
        }
        try {
            ResultSet rs = jdbcProvider.executeQuery(select);
            if (rs.next())
                return new JDBCDocumentRecord(table, rs);
            else
                throw new JDBCException("No new record!");
        } catch (Exception e) {
            throw new JDBCException(toString() + " Loading Saved Record", e);
        }
    }

    @Override
    public String toString() {
        return JDBCDocument.class.getSimpleName() + "[" + table.getSchema()
                + "." + table.getName() + "]";
    }
}
